package com.scalesoft.artsync.plugins.importer;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.scalesoft.artsync.frontend.ProgressDialog;
import com.scalesoft.artsync.frontend.Progressable;
import com.scalesoft.artsync.model.CONTENTLEVEL;
import com.scalesoft.artsync.model.submission.Submission;
import com.scalesoft.artsync.model.submission.artwork.ArtworkSubmission;
import com.scalesoft.artsync.modelimpl.submission.SubmissionCollection;
import com.scalesoft.artsync.modelimpl.submission.artwork.ArtworkSubmissionImpl;
import com.scalesoft.artsync.plugins.AccountSettings;
import com.scalesoft.artsync.plugins.Importer;
import com.scalesoft.artsync.plugins.PluginRegistry;
import com.scalesoft.artsync.plugins.settings.FuraffinitySettings;
import com.scalesoft.artsync.plugins.settings.ProxySettings;
import com.scalesoft.artsync.plugins.util.Log;
import com.scalesoft.artsync.plugins.util.ParserUtil;
import com.scalesoft.artsync.util.Notifyable;

public class FuraffinityImporter implements Importer, Runnable, ActionListener {

	private static String browserString = "Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2) Gecko/20100115 Firefox/3.6";
	private Progressable progressHandler;
	private Notifyable notifyable;
	private boolean cancel;

	static {
		PluginRegistry.registerImporter(new FuraffinityImporter());
//TODO load from properties or settings dialog or something
//		ProxySettings.setUseProxy(true);
//		ProxySettings.setProxyHost("10.17.3.11");
//		ProxySettings.setProxyPort(3128);
	}
	
	@Override
	public String getPluginDisplayName() {
		return "FurAffinity";
	}

	private HashMap<String, String> loginService(String username, String password) throws Exception {
		Log.i(this, "Logging in user " + username);
		String urlString = "https://www.furaffinity.net/login"; 
		URL url;
		if (ProxySettings.isUseProxy())
			url = new URL("https", ProxySettings.getProxyHost(), ProxySettings.getProxyPort(), urlString);
		else
			url = new URL(urlString);
			
		URLConnection conn = url.openConnection();
		conn.setRequestProperty("User-Agent", FuraffinityImporter.browserString);
		conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
		conn.addRequestProperty("Referer", "http://www.furaffinity.net/");
		conn.connect();
		Map<String, List<String>> headers = conn.getHeaderFields();
		List<String> values = headers.get("Set-Cookie");

		// InputStream is = conn.getInputStream();
		// int c;
		// while ((c = is.read()) != -1) {
		// System.out.print((char) c);
		// }
		// is.close();
		// Log.i(this, "----------------------------------------------------------------------------------------");

		String cookieNameUniq = "b=";
		String uniq = "";
		String cookieName = "a=";
		String sessionCookie = "";
		for (String v : values) {
			Log.i(this, "Login Phase 1 Result: " + v);
			uniq = v.substring(v.indexOf(cookieNameUniq) + cookieNameUniq.length(), v.indexOf(";", v
					.indexOf(cookieNameUniq)));
			if (uniq != null && uniq.length() > 0 && uniq.startsWith("="))
				uniq = uniq.substring(1);
			Log.i(this, "uniq: " + uniq);
		}
		urlString = "https://www.furaffinity.net/login/?url=/";
		if (ProxySettings.isUseProxy())
			url = new URL("https", ProxySettings.getProxyHost(), ProxySettings.getProxyPort(), urlString);
		else
			url = new URL(urlString);

		HttpURLConnection conn2 = (HttpURLConnection) url.openConnection();
		conn2.setDoOutput(true);
		conn2.setDoInput(true);
		conn2.setUseCaches(false);

		conn2.setInstanceFollowRedirects(false);
		HttpURLConnection.setFollowRedirects(false);

		conn2.setRequestProperty("Cookie", "b=" + uniq);
		conn2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		conn2.setRequestProperty("User-Agent", FuraffinityImporter.browserString);
		conn2.addRequestProperty("Referer", "https://www.furaffinity.net/login/");

		DataOutputStream printout = new DataOutputStream(conn2.getOutputStream());
		String content = "action=login&retard_protection=1&name=" + URLEncoder.encode(username, "UTF-8") + "&pass="
				+ URLEncoder.encode(password, "UTF-8") + "&login=Login+to%C2%A0FurAffinity";
		printout.writeBytes(content);
		printout.flush();
		printout.close();

		conn2.connect();
		String headerName = null;
		values = new ArrayList<String>();
		for (int i = 1; (headerName = conn2.getHeaderFieldKey(i)) != null; i++) {
			if (headerName.equals("Set-Cookie")) {
				String cookie = conn2.getHeaderField(i);
				values.add(cookie);
			}
		}

		// InputStream is2 = conn2.getInputStream();
		// while ((c = is2.read()) != -1) {
		// System.out.print((char) c);
		// }

		for (String v : values) {
			Log.i(this, "Login Phase 2 Result: " + v);
			if (v.indexOf(cookieName) >= 0) {
				sessionCookie = v.substring(v.indexOf(cookieName) + cookieName.length(), v.indexOf(";", v
						.indexOf(cookieName)));
				if (sessionCookie != null && sessionCookie.length() > 0 && sessionCookie.startsWith("="))
					sessionCookie = sessionCookie.substring(1);
				Log.i(this, "session: " + sessionCookie);
			}
		}

		HashMap<String, String> cookies = new HashMap<String, String>();
		cookies.put("fa_sessionid", sessionCookie);
		cookies.put("uniq", uniq);

		return cookies;
	}

	private int fetchSubmissionList(String username, HashMap<String, String> cookies)
			throws Exception {
		//username = "zen"; // TODO: REMOVE BEFORE RELEASE :-)
		int resultCount = 0;
		int pageResultCount;
		int page = 1;
		do {
			if (cancel) {
				progressHandler.hide();
				return 0;
			}
			progressHandler.setMinimum(0);
			progressHandler.setMaximum(100);
			progressHandler.setValue(0);
			progressHandler.setMessage("Fetching list of page "+page);
			pageResultCount = fetchSingleSubmissionListPage(username, cookies, page++);
			resultCount += pageResultCount;
		} while (pageResultCount > 0);
		return resultCount;
	}

	private int fetchSingleSubmissionListPage(String username, HashMap<String, String> cookies,
			int page) throws Exception {
		//Log.i(this, "a=" + cookies.get("fa_sessionid") + "; b=" + cookies.get("uniq") + ";");
		Log.i(this, "Fetching submission list for user " + username + " page " + page);
		ArrayList<Submission> result = new ArrayList<Submission>(SubmissionCollection.getInstance().getSubmissions());
		String urlString = "http://www.furaffinity.net/gallery/" + convertFAUsername(username) + "/" + page;
		URL url; 
		if (ProxySettings.isUseProxy())
			url = new URL("http", ProxySettings.getProxyHost(), ProxySettings.getProxyPort(), urlString);
		else
			url = new URL(urlString);

		URLConnection conn = url.openConnection();
		conn.setRequestProperty("User-Agent", FuraffinityImporter.browserString);
		conn.setRequestProperty("Cookie", "a=" + cookies.get("fa_sessionid") + "; b=" + cookies.get("uniq") + ";");
		conn.setRequestProperty("Referer", "http://www.furaffinity.net/");
		BufferedReader is = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		StringBuilder content = new StringBuilder();
		String inputLine;
		while ((inputLine = is.readLine()) != null) {
			content.append(inputLine);
		}
		is.close();
//		Log.i(this, "Content: "+content.toString());

		String data = content.toString();

		String submissionIdStart = "<a href=\"/view/";
		String submissionIdEnd = "/\">";
		int curpos = 0;
		int curSubmission = 0;
		int numSubmissions = 32;
		while (data.indexOf(submissionIdStart, curpos) > 0) {
			if (cancel) {
				progressHandler.hide();
				return 0;
			}
			int positionOfId = data.indexOf(submissionIdStart, curpos) + submissionIdStart.length();
			int positionOfIdEnd = data.indexOf(submissionIdEnd, positionOfId);
			String submissionId = data.substring(positionOfId, positionOfIdEnd);
			curpos = positionOfIdEnd + submissionIdEnd.length();

			Log.i(this, "Found submission ID: " + submissionId);
			progressHandler.setMinimum(0);
			progressHandler.setMaximum(numSubmissions);
			progressHandler.setValue(curSubmission);
			progressHandler.setMessage("Downloading ID "+submissionId);

			boolean submissionExists = false;
			for (Submission s : SubmissionCollection.getInstance().getSubmissions()) {
				if (s.getIdForService(getPluginDisplayName()).equals(submissionId)) {
					submissionExists = true;
					break;
				}
			}
			if (!submissionExists) {
				progressHandler.setMessage("Downloading image for ID "+submissionId);
				HashMap<String, String> imageData = fetchImageMetadata(cookies, submissionId);
				if (imageData != null) {
					ArtworkSubmission artworkSubmission = new ArtworkSubmissionImpl();
					String imageUrl = imageData.get("imageUrl");
					String contentLevel = imageData.get("contentLevel");
					String tags = imageData.get("tags");
					String title = imageData.get("title");
					String fileExtension = fetchImageData(cookies, imageUrl, submissionId, artworkSubmission.getId());
					artworkSubmission.setIdForService(getPluginDisplayName(), submissionId);
					artworkSubmission.setTitle(title);
					artworkSubmission.setDescription("Uploaded with <a href=\"http://code.google.com/p/artsync/\">ArtSync</a>");
					artworkSubmission.setFilename(artworkSubmission.getId() + "." + fileExtension);
					artworkSubmission.setContentLevel(CONTENTLEVEL.valueOf(contentLevel));
					artworkSubmission.setTags(tags);
					result.add(artworkSubmission);
					SubmissionCollection.saveSubmissionData(result);
					artworkSubmission.getIcon();
					notifyable.notifyListener();
					progressHandler.setMessage("Throttling for 2 seconds");
					Thread.sleep(2000);
				} else {
					Log.i(this, "Skipped submission " + submissionId + " - Not a supported submission type!");
				}
			} else {
				Log.i(this, "Skipped submission " + submissionId + " - I already have that one!");
			}
			curSubmission++;
		}

		return curSubmission;
	}

	protected HashMap<String, String> fetchImageMetadata(HashMap<String, String> cookies, String submissionid)
			throws Exception {
		Log.i(this, "Fetching image metadata for submission id " + submissionid);
		HashMap<String, String> result = new HashMap<String, String>();
		URL url;
		String urlString = "http://www.furaffinity.net/full/" + submissionid + "/";
		if (ProxySettings.isUseProxy())
			url = new URL("http", ProxySettings.getProxyHost(), ProxySettings.getProxyPort(), urlString);
		else
			url = new URL(urlString);

		URLConnection conn = url.openConnection();
		conn.setRequestProperty("User-Agent", FuraffinityImporter.browserString);
		conn.setRequestProperty("Cookie", "a=" + cookies.get("fa_sessionid") + "; b=" + cookies.get("uniq") + ";");
		conn.setRequestProperty("Referer", "http://www.furaffinity.net/");
		BufferedReader is = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		StringBuilder content = new StringBuilder();
		String inputLine;
		while ((inputLine = is.readLine()) != null) {
			content.append(inputLine);
		}
		is.close();
		//Log.i(this, "Content: "+content.toString());

		String data = content.toString();

		String submissionTitleStart = "<img id=\"submissionImg\" title=\"Click to change the View\" alt=\"";
		String submissionTitleEnd = "\" src=\"";
		String submissionTitle = ParserUtil.parseSection(data, submissionTitleStart, submissionTitleEnd, 0);
		if (submissionTitle != null) {
			Log.i(this, "Found submission Title: " + submissionTitle);
			result.put("title", submissionTitle);
		} else {
			//No image submission, abort!
			return null;
		}

		String submissionUrlStart = "var small_url = \"";
		String submissionUrlEnd = "\";";
		String submissionUrl = ParserUtil.parseSection(data, submissionUrlStart, submissionUrlEnd, 0);
		if (submissionUrl != null) {
			Log.i(this, "Found submission URL: " + submissionUrl);
			result.put("imageUrl", submissionUrl);
		}

		String submissionLevelStart = "&nbsp;&nbsp;&nbsp;&nbsp; <b>Theme:</b> ";
		String submissionLevelEnd = "<br/>";
		String submissionLevel = ParserUtil.parseSection(data, submissionLevelStart, submissionLevelEnd, 0);
		if (submissionLevel != null) {
			Log.i(this, "Found submission Level: " + submissionLevel);
			result.put("contentLevel", submissionLevel);
		}

		String submissionTagStart = "<a href=\"/search/@keywords ";
		String submissionTagEnd = "\">";
		int curpos = 0;
		StringBuilder tags = new StringBuilder();
		while (data.indexOf(submissionTagStart, curpos) > 0) {
			int position = data.indexOf(submissionTagStart, curpos) + submissionTagStart.length();
			int positionEnd = data.indexOf(submissionTagEnd, position);
			String submissionTag = data.substring(position, positionEnd);
			curpos = positionEnd + submissionTagEnd.length();

			Log.i(this, "Found submission Tag: " + submissionTag);
			tags.append(submissionTag + ", ");
		}
		
		String genderStart = "&nbsp;&nbsp;&nbsp;&nbsp; <b>Gender:</b> ";
		String genderEnd = "<br/>";
		String genderFA = ParserUtil.parseSection(data, genderStart, genderEnd, 0);
		if (genderFA != null) {
			Log.i(this, "Found FA Gender: " + genderFA);
			if (genderFA.trim().equalsIgnoreCase("Multiple characters"))
				tags.append("Bisexual, ");
			else if (genderFA.trim().equalsIgnoreCase("Any"))
				tags.append("Other, ");
			else if (genderFA.trim().equalsIgnoreCase("Male"))
				tags.append("Male, ");
			else if (genderFA.trim().equalsIgnoreCase("Female"))
				tags.append("Female, ");
			else if (genderFA.trim().equalsIgnoreCase("Herm"))
				tags.append("Hermaphrodite, ");
			else if (genderFA.trim().equalsIgnoreCase("Transgender"))
				tags.append("Other, ");
			else if (genderFA.trim().equalsIgnoreCase("Other / Not Specified"))
				tags.append("Other, ");
			else
				tags.append("Other, ");
		}
		result.put("tags", tags.toString());

		if (data.indexOf("/img/labels/mature.gif") > 0 || data.indexOf("/img/labels/adult.gif") > 0) {
			result.put("contentLevel", CONTENTLEVEL.ADULT.name());
			Log.i(this, "Submission is ADULT");
		}
		else {
			result.put("contentLevel", CONTENTLEVEL.CLEAN.name());
			Log.i(this, "Submission is CLEAN");
		}
		return result;
	}

	protected String fetchImageData(HashMap<String, String> cookies, String submissionurl, String submissionid,
			String internalId) throws Exception {
		Log.i(this, "Fetching image for submission id " + submissionid);
		URL url;
		if (ProxySettings.isUseProxy())
			url = new URL("http", ProxySettings.getProxyHost(), ProxySettings.getProxyPort(), submissionurl);
		else
			url = new URL(submissionurl);
		
		URLConnection conn = url.openConnection();
		conn.setRequestProperty("User-Agent", FuraffinityImporter.browserString);
		conn.setRequestProperty("Cookie", "a=" + cookies.get("fa_sessionid") + "; b=" + cookies.get("uniq") + ";");
		conn.setRequestProperty("Referer", "http://www.furaffinity.net/view/" + submissionid + "/");
		int contentLength = conn.getContentLength();
		InputStream raw = conn.getInputStream();
		InputStream in = new BufferedInputStream(raw);
		byte[] data = new byte[contentLength];
		int bytesRead = 0;
		int offset = 0;
		while (offset < contentLength) {
			bytesRead = in.read(data, offset, data.length - offset);
			if (bytesRead == -1)
				break;
			offset += bytesRead;
		}
		in.close();

		if (offset != contentLength) {
			throw new IOException("Only read " + offset + " bytes; Expected " + contentLength + " bytes");
		}

		String extension = submissionurl.substring(submissionurl.lastIndexOf('.') + 1);

		FileOutputStream out = new FileOutputStream("files/" + internalId + "." + extension);
		out.write(data);
		out.flush();
		out.close();
		return extension;
	}

	@Override
	public void setProgressHandler(Progressable progressHandler) {
		this.progressHandler = progressHandler;
	}

	@Override
	public void run() {
		cancel = false;
		File dircheck = new File("files/thumbnails");
		if (!dircheck.exists() || !dircheck.isDirectory()) {
			Log.i(this, "creating dir");
			dircheck.mkdirs();
		}
		progressHandler.setActionListener(this);
		progressHandler.setMinimum(0);
		progressHandler.setMaximum(100);
		progressHandler.setValue(50);
		progressHandler.setMessage("Logging in...");
		progressHandler.show();
		FuraffinitySettings settings = FuraffinitySettings.getInstance();
		settings.loadSettings();
		HashMap<String, String> cookies;
		try {
			cookies = loginService(settings.getUsername(), settings.getPassword());
			fetchSubmissionList(settings.getUsername(), cookies);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			Log.e(this, "Error in run()", e);
		}
		progressHandler.hide();
	}

	@Override
	public AccountSettings getSettings() {
		return FuraffinitySettings.getInstance();
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		if (event.getSource() == ProgressDialog.getInstance().getButtonCancel()) {
			cancel = true;
		}
		
	}

	@Override
	public void setNotifyable(Notifyable notifyable) {
		this.notifyable = notifyable;
	}

	//Converts from real usernames to the username URLs used on FA
	private String convertFAUsername(String name) {
		name = name.replaceAll("_", "");
		return name;
	}
	

}
